const cors = require("cors");
const express = require("express");
const wechatOauth = require("wechat-oauth");
const session = require("express-session");
const axios = require("axios");
const bodyParser = require("body-parser");
const xlsx = require("xlsx");
const path = require("path");
const moment = require("moment");
const uuid = require("uuid");
const https = require("https");
const eae = require("express-async-errors");
const hbs = require("hbs");
const helmet = require('helmet');
const canvas = require("canvas");

const {URL} = require('url');

const cache = require("./cache");

const app = express();
const wework = require("./wework.js");
const tutils = require("./tutils.js");
const rds = require("./rds.js");
const logger = require("./logger");
const task = require("./task");

var client = new wechatOauth("wx514b6772d818b3a7", process.env.MP_APP_SECRET);
var signature = require("wx_jsapi_sign");
var wxJsApiConfig = require("./wx-js-api-config")();

const ghostAPIUrl = "https://mtx.totemlife.cn/ghost/api/v3";
const ghostAPIKey = process.env_GHOST_API_KEY;

const fs = require("fs-extra");
const { PDFDocument, StandardFonts, rgb } = require("pdf-lib");
const QRCode = require("qrcode");

app.use(helmet.noSniff());
app.use(helmet.hsts({maxAge: 7776000}));
app.disable('x-powered-by');

//必须注册在view engine之前, 不支持-连接的文件名
hbs.registerPartials( path.join(__dirname, 'views/partials'), {extname:'.hbs'} );

app.set("view engine", "hbs");
app.set("views", __dirname + "/views"); //using view directory

app.set("view autoescape", true); //not escape special chars such as '&' when render to template
app.enable("trust proxy"); //so can get real ip of client instead of ::ffff:127.0.0.1

hbs.registerHelper({
	parseJson: (json) => { try { return JSON.parse(json); } catch (e) { return {}; } },
	json: function(context) { return JSON.stringify(context); },
	eq: function (a, b) { return a == b; },             // 注意这里覆盖了原来的 === 逻辑
	neq: function (v1, v2) { return v1 !== v2; },
	gt: function (v1, v2) { return v1 > v2; },
	lt: function (v1, v2) { return v1 < v2; },
	gte: function (v1, v2) { return v1 >= v2; },
	lte: function (v1, v2) { return v1 <= v2; },
	and: function () { return Array.prototype.slice.call(arguments, 0, -1).every(Boolean); },
	or: function () { return Array.prototype.slice.call(arguments, 0, -1).some(Boolean); },
	formatDate: function (date, format) { return moment(date).format(format || 'YYYY-MM-DD'); },
	selected: (value, target) => value === target ? 'selected' : '',
	isArray: function(value) { return Array.isArray(value); },
	join: function(array, separator) { return Array.isArray(array) ? array.join(separator) : array; },
	split: function(str, separator) { return str ? str.split(separator) : []; },
	add: function(a, b) { return parseInt(a) + parseInt(b); },
	divide: function(a, b) { return a / b; },
	multiply: function(a, b) { return a * b; },
	formatPercentage: function(value) { return (value * 100).toFixed(1); },
	formatNumber: function(value, decimals) { return value == null ? '' : Number(value).toFixed(decimals || 0); },
	currentYear: () => new Date().getFullYear(),
	times: function(n, block) { let accum = ''; for(let i = 0; i < n; ++i) accum += block.fn(i); return accum; },
	math: function(lvalue, operator, rvalue) {          //eg. {{math 1 "+" 2}}
		lvalue = parseFloat(lvalue); rvalue = parseFloat(rvalue);
		switch(operator) {
			case '+': return lvalue + rvalue; case '-': return lvalue - rvalue;
			case '*': return lvalue * rvalue; case '/': return lvalue / rvalue;
			case '%': return lvalue % rvalue; default: return lvalue;
		}
	},
	maskNa: function(str) { return str },   // 啥也不遮，用于方便随时替换为maskMi
	maskMi: function(str) {                 // 新增：将字符串中间的1/2部分替换为星号，保留首尾各1/4的字符
		if (!str || typeof str !== 'string') return '';
		if (str.length <= 2) return str; // 字符串太短不处理
		// 计算前后各保留的字符数（各占1/4）
		const keepChars = Math.max(1, Math.floor(str.length / 4));
		// 计算中间需要替换的字符数（占2/4）
		const maskLength = str.length - (keepChars * 2);
		const mask = '*'.repeat(maskLength);
		return str.substring(0, keepChars) + mask + str.substring(str.length - keepChars);
	},
});

var corsOptions = {
	origin: "*", // 允许任何源
	methods: "GET, HEAD, PUT, PATCH, POST, DELETE",
	allowedHeaders: "Content-Type, Authorization",
	credentials: true,
};

// 使用cors中间件
app.use(cors(corsOptions));

//在session中存储的rid是指推荐者在totem_mall中对应的uid，但以字符形式存储
//在session中存储的totemUID则是指用户自己在totem_mall中对应的uid，是数值形式
app.use(
	session({
		name: "mta-apps",
		secret: "start-with-wechat-oauth",
		saveUninitialized: true,
		resave: true,
		httpOnly: false,
		cookie: {
			expires: new Date(253402300000000),             // Approximately Friday, 31 Dec 9999 23:59:59 GMT
			//secure: process.env.NODE_ENV === 'production'   // 添加 secure 属性
			secure: true
		},
	})
);

// Use the body-parser module to parse the posted data
app.use(bodyParser.urlencoded({ extended: true }));
// Use to parse JSON posted body
app.use(express.json());

app.use("/js", express.static(__dirname + "/js"));
app.use("/css", express.static(__dirname + "/css"));
app.use("/public", express.static(__dirname + "/public"));

function startsWithAny(str, prefixes) {
	return prefixes.some((prefix) => str.startsWith(prefix));
}

const mtaAuthMiddleware =
	(app = "crm", roleAsked = "crm") =>
	(req, res, next) => {
		const user = req?.session?.user ?? undefined;
		const appNames = {
			crm: "ENG协作系统",
			ai: "图腾AI服务",
			tools: "图腾工具服务",
			loyalty: "图腾会员服务",
			trial: "临床试验服务",
			space: "星际探索计划",
			api: "图腾应用接口",
            card: "名片展示系统",
		};
		const title = appNames[app] || "图腾应用登陆";

		if (user) {
			const roles = user.roles || "";
			if (roles.includes(roleAsked)) {
				next();
				return;
			}
			const tips = `mta-${app} uid#${user.uid} ip ${req.ip}`;
			const message = "您的应用权限不足";
			const description = "请联系人事支持hr@totembio.cn申请权限";
			res.render("warn", { title, message, description, tips });
		} else {
			let dest_url =
				req.protocol +
				"://" +
				req.get("host") +
				"/apps" +
				req.originalUrl;
			req.session.dest_url = dest_url;
			req.session.rid = req.query.rid || "1656";
			req.session.title = title;
			req.session.save();
			const ua = req.headers["user-agent"] || "AnonymousUserAgent"; // in case such as mp-api post request from tmall server
			const isWechat = ua.includes("MicroMessenger");
			const isWxwork = ua.includes("wxwork");
			const baseUrl = `${req.protocol}://${req.get("host")}/apps`;
			let authUrl = baseUrl + "/login"; //默认认证URL https://totemlife.cn/apps/login
			if (isWechat) authUrl = baseUrl + "/oauth";
			if (isWxwork) authUrl = baseUrl + "/wk-auth";
			res.redirect(authUrl);
		}
	};

const cgiRouter = require("./routes/cgi-bin"); // 假设cgi.js是你的CGI模块文件
// 使用app.use()注册cgi模块的路由，不做权限校验
app.use("/cgi-bin", cgiRouter);

const dataRouter = require("./routes/data");
app.use("/data", dataRouter);

const apiRouter = require("./api.js");
app.use("/api", mtaAuthMiddleware("api", "crm"), apiRouter); //要求crm系统权限，即用户roles中包括有crm-xxx

const adminRouter = require('./admin');
app.use("/admin", mtaAuthMiddleware("api", "crm"), adminRouter); //要求crm系统权限，即用户roles中包括有crm-xxx

const crmRouter = require("./crm.js");
app.use("/crm", mtaAuthMiddleware("crm", "crm"), crmRouter);

const solutionRouter = require('./routes/solution');
app.use("/solution", (req, res, next) => {
    // 添加目标标识到请求对象中
    req.target = req.query.target || '';
    next();
}, solutionRouter);

const cardRouter = require('./routes/card');
app.use("/card", mtaAuthMiddleware("card",""), cardRouter);

const helpdeskRouter = require('./routes/helpdesk');
app.use("/helpdesk", helpdeskRouter);

const aiRouter = require("./ai.js");
app.use("/ai", mtaAuthMiddleware("ai", ""), aiRouter);
app.use("/tb", mtaAuthMiddleware("ai", ""), aiRouter);
app.use("/gpt", mtaAuthMiddleware("ai", ""), aiRouter);

// 游戏路由
const spaceJourney = require('./routes/space-journey');
app.use('/space-journey', mtaAuthMiddleware("space", ""), spaceJourney);

// 工具路由
const toolsRouter = require('./routes/tools');
app.use('/tools', mtaAuthMiddleware("tools", ""), toolsRouter);

// 量表路由
const scalesRouter = require('./routes/scales');
app.use('/scales', mtaAuthMiddleware("tools", ""), scalesRouter);

const trialRouter = require("./routes/trial.js");
app.use("/trial", mtaAuthMiddleware("trial", ""), trialRouter);

// ERP路由
const erpRouter = require('./routes/erp');
app.use("/erp", erpRouter);

// 医生业绩路由
const doctorPerformanceRouter = require('./routes/doctor-performance');
app.use("/doctor-performance", mtaAuthMiddleware("crm", "crm"), doctorPerformanceRouter);

//专区服务，必须允许用户不注册可访问，这样SEO才能工作
const asdRouter = require("./asd.js");
app.use("/asd", asdRouter);


const loyaltyRouter = require("./loyalty.js");
app.use("/loyalty", mtaAuthMiddleware("loyalty", ""), loyaltyRouter);

// Error handling middleware
app.use((err, req, res, next) => {
	console.error(err);
	res.render("warn", {
		message: "应用服务出错",
		description: err.toString(),
		tips: "请联系技术支持app@totembio.cn处理故障",
	});
});

function isWechatAuthRequiredRequest(req) {
	const str = req.url;
	const ua = req.headers["user-agent"] || "AnonymousUserAgent"; // in case such as mp-api post request from tmall server

	let arrs = [
		"download-doc",
		"/show-card",
		"/ref-card",
		"/daily-poster-entry",
		"/show-daily-checkin-card",
		"/ci2",
		"/write-card",
		"/sign-in",
		"/b",
	];
	if (
		startsWithAny(str, arrs) &&
		ua.indexOf("MicroMessenger") > -1 &&
		!req.session.totemUID
	)
		return true;

	//每次都要强制刷新register-mta以获得totemUID的
	arrs = ["/bind-wework"];
	if (startsWithAny(str, arrs) && ua.indexOf("MicroMessenger") > -1)
		return true;

	return false;
}

app.use(async function (req, res, next) {
	const ua = req.headers["user-agent"] || "AnonymousUserAgent"; // in case such as mp-api post request from tmall server
	const isWechat = ua.includes("MicroMessenger");
	const isWxwork = ua.includes("wxwork");
	const wlient = isWxwork
		? "wxwork-client"
		: isWechat
		? "wechat-client"
		: "other-client"; //注意isWxwork必须在前

	const baseUrl = `${req.protocol}://${req.get("host")}/apps`;
	const authUrl = ua.includes("wxwork")
		? `${baseUrl}/wk-auth`
		: `${baseUrl}/oauth`;
	const rid = req.query.rid || "1414";

	//refer应用有特定的代码所以需要特殊处理
	if (req.url.indexOf("/refer") === 0) {
		//  如果在微信环境中，还要进一步对所有未通过微信授权的请求做授权处理
		if (ua.indexOf("MicroMessenger") > -1 && !req.session.totemUID) {
			dest_url = `${baseUrl}${req.url}`;
			req.session.dest_url = dest_url;
			req.session.rid = rid;
			req.session.save();

			//logger.info(`REFER-ASK-AUTH: rid# ${rid} | uid# ${req.session.totemUID || 0} | ${wlient} ${req.ip} | ${dest_url} | ${req.url}`);
			res.redirect(authUrl);

			return;
		}

		//logger.info(`REFER-SKIP-AUTH: rid# ${rid} | uid# ${req.session.totemUID || 0} | ${wlient} ${req.ip} | ${req.url}`);
	}

	if (isWechatAuthRequiredRequest(req)) {
		let dest_url = req.query.dest
			? decodeURIComponent(req.query.dest)
			: baseUrl + req.originalUrl;
		req.session.dest_url = dest_url;
		req.session.rid = rid;
		req.session.wk_openid = req.query.wk_openid || ""; //in case using wk_openid
		req.session.wk_userid = req.query.wk_userid || "";
		req.session.save();

		//logger.info(`AUTH-REQUIRED: rid# ${rid} | ${req.ip} request ${req.url} | dest_url: ${dest_url} | authUrl: ${authUrl}`);
		res.redirect(authUrl);
		return;
	}

	next();
});

app.get("/", (req, res) => {
	res.render("warn", {
		title: "医用二代菌临床前沿",
		message: "患者的笑容是我们最好的回报",
		description: "欢迎来到营养微生态临床应用最前沿",
		icon: "weui-icon-success weui-icon_msg",
	});
});

app.get("/go-to-weapp", async (req, res) => {
	// 加载小程序路径配置
	const weappConfig = require("./data/weapp-modules.json");
	const viewName = req.query.view || "platform";

	// 从配置中获取路径信息
	const pathInfo = weappConfig.paths[viewName] || {};
	const path = pathInfo.path || weappConfig.defaultPath;

	// 处理推广者ID
	const spreaderParam = weappConfig.queryParams.spreader.find(param => req.query[param]);
	const spreader = req.query[spreaderParam] || weappConfig.defaultSpreader;

	// 构建查询字符串
	let query = `spreader_uid=${spreader}`;

	// 添加可选参数
	weappConfig.queryParams.optional.forEach(param => {
		if (req.query[param]) {
			query += `&${param}=${req.query[param]}`;
		}
	});

	// 添加特定视图的额外查询参数
	if (pathInfo.additionalParams) {
		Object.entries(pathInfo.additionalParams).forEach(([key, value]) => {
			if ( key === 'url' ){
				//对于/page/web-view/index?是，URL需要做编码
				query += `&${key}=`;
				query += encodeURIComponent(value.includes('?') ? `${value}&rft=${spreader}` : `${value}?rft=${spreader}`);
			}else{
				query += `&${key}=${value}`;
			}
		});
	}

	// 准备参数
	let args = { path, query, is_expire: true };

	// 检测客户端设备类型
	const userAgent = req.headers['user-agent'] || '';
	const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);

	if (!isMobile) {
		// PC 设备使用 weixin 协议链接
		const appid = 'wx1b441d1a37b278e1'; // 小程序的 appid
		const env_version = 'release'; // 环境版本
		const formattedPath = path.startsWith('/') ? path.substring(1) : path; // 去掉开头的斜杠
		const weixinProtocolUrl = `weixin://dl/business/?appid=${appid}&path=${formattedPath}${query ? '&query=' + encodeURIComponent(query) : ''}&env_version=${env_version}`;
		console.log("PC Client detected, redirecting to weixin protocol URL:", weixinProtocolUrl);
		return res.redirect(weixinProtocolUrl);
	}

	// 移动设备继续使用原有逻辑
	console.log("Mobile Client detected, go-to-weapp with args: ", args);
	const urlLink = await tutils.genWeappUrlLink(args);

	// 如果成功获取URL，则重定向用户
	if (urlLink) {
		res.redirect(urlLink);
	} else {
		res.status(500).send(
			"Failed to generate URL link for WeChat Mini Program."
		);
	}
});

app.get("/b/:code", async (req, res) => {
	logger.info(`QuickLauncher-B called : ${req.ip} requested ${req.url}`);
	const code = req.params.code;
	if (code == "luck-money-2024") {
		const url = `https://doc.weixin.qq.com/forms/AIEAOAfVAAgANoAOAaMAOwgOPkjfPhSLf`;
		return res.redirect(302, url);
	}
	if (code == "240625") {
		const url = "https://ynxqr.xetlk.com/sl/MNUkj";
		return res.redirect(302, url);
	}
	if (code == "futang") {
		return res.redirect(
			302,
			"https://mtx.totemlife.cn/add-customer-service-wechat-to-get-free-consultation/"
		);
	}
	const url = `https://totemlife.cn/apps/loyalty/blessing/${code}`;
	return res.redirect(302, url);
});

app.get("/x/:slug", async (req, res) => {
	logger.info(`QuickLauncher-X called : ${req.ip} requested ${req.url}`);
	let slug = req.params.slug;
	let url = `https://mtx.totemlife.cn/${slug}/`;
	//if ( slug == 'daily' ) url = `https://mtx.totemlife.cn/gut-battle-probiotics-dark-before-dawn-herxheimer-reaction/`;
	if (slug == "daily") {
		const sg = await tutils.getMostRecentSlug();
		url = `https://mtx.totemlife.cn/${sg}/`;
	}
	if (slug == "recruit") {
		url = `https://mtx.totemlife.cn/autism-consultant-job-application-guide/`;
	}
	if (slug == "random") {
		url =
			"https://mtx.totemlife.cn/" +
			(await tutils.getSlugBasedOnDate(moment()));
	}
	return res.redirect(302, url);
});

//quick-launcher L - 研讨会
app.get("/l", async (req, res) => {
	res.redirect(302, "https://totemlife.cn/apps/loyalty/conference/list");
});

//quick-launcher C - contact us
app.get("/c", async (req, res) => {
	res.redirect(302, "https://totemlife.cn/apps/go-to-weapp?view=helpdesk");
});

//quick-launcher D - 关注公众号
app.get("/d", async (req, res) => {
	res.redirect(302, "https://totemlife.cn/apps/go-to-weapp?view=platform");
});

//Quick-launcher Event
app.get("/e", async (req, res) => {
	logger.info(`QuickLauncher-E called : ${req.ip} requested ${req.url}`);
	const url = `https://totemlife.cn/apps/loyalty/event`;
	return res.redirect(302, url);
});

//mp account follow
app.get("/m", async (req, res) => {
	logger.info(`QuickLauncher-M called : ${req.ip} requested ${req.url}`);
	//强制退出用户登陆状态
	const page = "会员注册已成功";
	const description =
		"会员注册完成后，首次注册成功关注公众号可领取1000健康金豆";
	const title = "请领取首次注册礼包";
	const qr_code_url = "https://oss.foodtrust.cn//e6c74202405020804319178.png";
	const tips = "长按二维码进入公众号领取";
	const message = "首次注册成功可得1000健康金豆";
	return res.render("follow", {
		page,
		description,
		qr_code_url,
		title,
		message,
		tips,
	});
});

//这个路由的数据由tmall中的公众号监听程序在收到数据时候发送过来
app.post("/mp-api", async (req, res) => {
	const { body } = req;
	if (
		body.EventKey &&
		(body.Event === "SCAN" || body.Event === "subscribe")
	) {
		logger.info(`mp-api scan event: `, req.body);
		const isScanEvent = body.Event === "SCAN";
		const channel_id = isScanEvent
			? parseInt(body.EventKey)
			: parseInt(body.EventKey.split("_")[1]);
		const openid = body.FromUserName;

		const currentTime = Math.floor(Date.now() / 1000); // Current Unix timestamp
		const fiveMinutesAgo = currentTime - 300; // Five minutes ago in Unix timestamp

		// Update the record in the mp_authentication table
		const updateSql = ` UPDATE tutem_mall.mp_authentication SET wx_server_response = ?  WHERE channel_id = ? AND is_used = 1 AND apply_start_time >= ?  ORDER BY apply_start_time DESC LIMIT 1 `;
		const [result] = await rds.query(updateSql, [
			openid,
			channel_id,
			fiveMinutesAgo,
		]);
		if (result.affectedRows > 0) {
			logger.info(
				`Updated wx_server_response for channel #${channel_id}, mp-api: `,
				req.body
			);
		}
	}

	res.status(200).send("Data received successfully.");
});

//接受tmall发过来的消息显示到logger中
app.post("/tmall-hook", async (req, res) => {
	const { body } = req;
	//logger.info(`TMALL-HOOK called by ${req.ip}`);
	if (body) {
		if (body.totemUID) {
		}
	}
	res.status(200).send("Data received successfully.");
});

//接受ghost发过来的消息显示到logger中
app.post("/wx-client-hook", async (req, res) => {
	const { body } = req;
	if (body) {
		let detail = "";
		if (typeof body == "object") {
			if (body.from) detail += `from: ${body.from}\n`;
			if (body.href) detail += `href: ${body.href}\n`;
			if (body.memo) detail += `memo: ${body.memo}\n`;
		} else {
			detail += `body: `;
			detail += JSON.stringify(body);
			detail += `\n`;
		}
		detail += `client ip ${req.ip}\n`;
		const uid = body.totemUID || 0;
		if (uid == 0) logger.info(`WX-CLIENT-HOOK: U#${uid} ${detail}`);
		task.notifyUserAction(body.totemUID, detail, "ACCESS-MTX"); //放下即走不等待
	}
	res.status(200).send("Data received successfully.");
});

// Route to handle /mp-auth POST request
app.post("/mp-auth", async (req, res) => {
	const { temp_uuid, channel_id } = req.body;

	// Query the mp_authentication table for the specified temp_uuid and channel_id
	const sql =
		"SELECT id, apply_start_time, wx_server_response FROM tutem_mall.mp_authentication WHERE is_used = 1 AND temp_uuid = ? AND channel_id = ?";
	const [rows] = await rds.query(sql, [temp_uuid, channel_id]);

	if (rows.length > 0) {
		const row = rows[0];
		const currentTime = Math.floor(Date.now() / 1000); // Current Unix timestamp
		const fiveMinutesAgo = currentTime - 300; // Five minutes ago in Unix timestamp
		if (row.apply_start_time < fiveMinutesAgo) {
			res.json({ timeout: true });
		} else {
			const openid = row.wx_server_response || null;
			res.json({ openid });
		}
	} else {
		res.json({ timeout: true });
	}
});

// Route to handle /mp-grant GET request
app.get("/mp-grant", async (req, res) => {
	const { openid, temp_uuid } = req.query;
	if (typeof openid === "undefined" || temp_uuid == "undefined") {
		const ua = req.headers["user-agent"] || "AnonymousUserAgent"; // in case such as mp-api post request from tmall server
		logger.info(`invalid mp-grant request from ${req.ip}`, ua);
		res.render("warn", {
			message: "用户认证错误",
			description: "认证凭据无效",
		});
		return;
	}

	const authSql =
		"SELECT id FROM tutem_mall.mp_authentication WHERE is_used = 1 AND temp_uuid = ? AND wx_server_response = ?";
	const [authRows] = await rds.query(authSql, [temp_uuid, openid]);
	if (authRows.length === 0) {
		res.render("warn", {
			message: "用户认证错误",
			description: "认证凭据无效",
		});
		return;
	}

	// Update the mp_authentication record for the given openid and temp_uuid
	const uSql =
		"UPDATE tutem_mall.mp_authentication SET is_used = 0, temp_uuid = null, apply_start_time = null, wx_server_response = null WHERE is_used = 1 AND temp_uuid = ? AND wx_server_response = ?";
	await rds.query(uSql, [temp_uuid, openid]);

	// Query eb_user table to find the user with the given openid
	const userSql = `SELECT u.uid, u.cc_user_id, u.real_name as name, u.roles, COALESCE(r.id, 0) as is_rep_id, COALESCE(r.code, '') as is_rep_code, COALESCE(r.team, '') as is_rep_team FROM tutem_mall.eb_user u left join eb_rep r on (u.uid = r.uid) WHERE u.uid IN (SELECT uid FROM eb_wechat_user WHERE openid = '${openid}')`;
	const [userRows] = await rds.query(userSql);

	if (userRows.length > 0) {
		// User found, set user data in session and redirect to dest_url
		const user = userRows[0];
		req.session.user = user;

		// AUTH OKEY 在所有MTA APPS中都要改为按以下凭据为准，重启后无效 - 公众号登录授权
		// USER-AUTH-1
		req.session.authUserInfo = {
			totemUID: user.uid,
			ccUID: user.cc_user_id,
			uid: user.uid,
			crmROLE: tutils.getCrmRole(user.roles),
			trialROLE: tutils.getTrialRole(user.roles),
			repID: user.is_rep_id,
			repCODE: user.is_rep_code,
			repTEAM: user.is_rep_team,
		};
		req.session.totemUID = user.uid;
		req.session.ccUID = user.cc_user_id;
		req.session.crmROLE = tutils.getCrmRole(user.roles);
		req.session.trialROLE = tutils.getTrialRole(user.roles);
		req.session.repID = user.is_rep_id;
		req.session.repCODE = user.is_rep_code;
		req.session.repTEAM = user.is_rep_team;

		req.session.save();

		delete req.session.temp_uuid;
		logger.info(`mp-grant authenticate user: `, req.session.user);
		//res.redirect(req.session.dest_url || "https://mtx.totemlife.cn/");
		const nextPath = req.session.dest_url || 'https://mtx.totemlife.cn/';
		const totemUID = req.session.totemUID;
		const rid = req.session.rid || "1604";
		res.render("forward", { totemUID, nextPath, rid });
		return;
	}

	// User not found, render error message
	res.render("warn", {
		message: "用户认证错误",
		description: "认证凭据无效",
	});
});

app.post("/get-signature", function (req, res) {
	var url = req.body.url;
	signature.getSignature(wxJsApiConfig)(url, function (error, result) {
		res.json(error ? { error: error } : result);
	});
});

app.get("/get-signature2", function (req, res) {
	var url = req.query.url;
	signature.getSignature(wxJsApiConfig)(url, function (error, result) {
		res.json(error ? { error: error } : result);
	});
});

app.get("/callback2", async (req, res) => {
	const host = req.query.host || "totemlife.cn";
	const code = req.query.code;
	const url = `https://${host}/apps/callback?code=${code}`;
	res.redirect(url);
});

app.get("/callback", async (req, res) => {
	const code = req.query.code;

	await client.getUserByCode(code, async function (err, wechatUserInfo) {
		if (wechatUserInfo) {
			const rid = req.session.rid || "1423";
			const wk_openid = req.session.wk_openid || "";
			const wk_userid = req.session.wk_userid || "";
			const data = { wechatUserInfo, rid, wk_openid, wk_userid };
			const response = await axios.post(
				"https://mall.totembio.cn/api/register_mta_user",
				data
			);
			const result = response.data;
			if (result && result.msg == "ok") {
				const user = result.data;
				const totemUID = user.uid;
				const ccUID = user.cc_user_id;
				let nextPath =
					req.session.dest_url || "https://mtx.totemlife.cn/";

				// 检查nextPath是否以特定的前缀开头
				const prefixes = [
					"https://mtx.totemlife.cn",
					"http://mtx.totemlife.cn",
				];
				const isMatch = prefixes.some((prefix) =>
					nextPath.startsWith(prefix)
				);
				// 如果匹配，添加或更新'rft'参数
				console.log("nextPath: ", nextPath);
				if (isMatch) {
					let url = new URL(nextPath);
					if (!url.searchParams.has("rft")) {
						url.searchParams.set("rft", totemUID);
					}
					nextPath = url.toString();
					console.log("add rft for nextPath: ", nextPath);
				}

				const info = `imge: [avatar](${user.wechatUserInfo?.headimgurl})\ndest: [${nextPath}](${nextPath})`;
				await task.notifyUserAction(totemUID, info, "WECHAT-CALLBACK"); //这里只通知对应的代表

				// AUTH OKEY 在所有MTA APPS中都要改为按以下凭据为准，重启后无效 - 微信授权
				// USER-AUTH-2
				req.session.authUserInfo = {
					totemUID,
					ccUID,
					uid: user.uid,
					crmROLE: tutils.getCrmRole(user.roles),
					trialROLE: tutils.getTrialRole(user.roles),
					repID: user.is_rep_id,
					repCODE: user.is_rep_code,
					repTEAM: user.is_rep_team,
				};
				req.session.totemUID = user.uid;
				req.session.ccUID = ccUID;
				req.session.crmROLE = tutils.getCrmRole(user.roles);
				req.session.trialROLE = tutils.getTrialRole(user.roles);
				req.session.repID = user.is_rep_id;
				req.session.repCODE = user.is_rep_code;
				req.session.repTEAM = user.is_rep_team;

				req.session.user = result.data;
				req.session.rid = rid;
				req.session.wechatUserInfo = wechatUserInfo; //TBO
				req.session.save();

				delete req.session.dest_url;

				res.render("forward", { totemUID, nextPath, rid });
				return;
			}
		}

		const dest = req.session.dest_url || "https://mtx.totemlife.cn/";
		delete req.session.dest_url;
		res.redirect(dest);
		return;
	});
});

// 回调路由：处理企业微信的回调请求
app.get("/wk-auth/callback", async (req, res) => {
	// 处理企业微信回调后的逻辑
	const { code, state } = req.query;
	// 这里可以根据 state 参数来做一些逻辑处理
	res.redirect(
		`https://totemlife.cn/apps/wk-auth?code=${code}&state=${state}`
	);
});

app.get("/oauth", async (req, res) => {
	// Redirect the user to the WeChat OAuth authorization page.
	// const callbackUrl = 'https://totemlife.cn/apps/callback';
	const host = req.get("host");
	const callbackUrl = `https://totemlife.cn/apps/callback2?host=${host}`; //必须是微信公众号后台配置的授权网页
	const authUrl = client.getAuthorizeURL(callbackUrl, "", "snsapi_userinfo");
	res.redirect(authUrl);
});

app.get("/cki", async (req, res) => {
	const url =
		"https://doc.weixin.qq.com/forms/AIEAOAfVAAgANoAOAaMAOw9dpAO0w6TMf";
	res.redirect(url);
});

// 认证过程：重定向到企业微信的认证页面
app.get("/wk-auth", async (req, res) => {
	const corpId = "wwdff5bd6a9e81395b";
	const agentId = "1000026";
	const secret = process.env.WK_ENG_SECRET;
	const redirectUri = "https://totemlife.cn/apps/wk-auth/callback"; // 这是你的回调URL
	const { code, state } = req.query;
	if (!code) {
		// 如果没有 code，则重定向到企业微信认证页面
		const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${corpId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&agentid=${agentId}&state=${state}#wechat_redirect`;
		res.redirect(authUrl);
	} else {
		// 根据 code 获取用户信息
		const accessToken = await wework.getWeworkAccessToken(1000026);
		const response = await axios.get(
			`https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=${accessToken}&code=${code}`
		);
		let userInfo = response.data;
		logger.info("WEWORK AUTHED: ", userInfo);
		let userid = userInfo.UserId;
		if (userid) {
			const userResponse = await axios.get(
				`https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=${accessToken}&userid=${userid}`
			);
			const {
				data: { openid },
			} = await axios.post(
				`https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid?access_token=${accessToken}`,
				{ userid: userid }
			);
			userInfo.OpenId = openid;
		}
		//对于没有userid的，默认应该会有openid
		const openid = userInfo.OpenId;
		//console.log('wk-auth: ', userInfo);
		if (openid) {
			const sql = `select u.* , w.wk_userid, COALESCE(r.id, 0) as is_rep_id, COALESCE(r.code, '') as is_rep_code, COALESCE(r.team, '') as is_rep_team from tutem_mall.eb_user u
				left join tutem_mall.eb_wechat_user w on (u.uid = w.uid)
				left join tutem_mall.eb_rep r on (u.uid = r.uid)
				where u.uid in (select uid from tutem_mall.eb_wechat_user where wk_openid = '${openid}') `;
			const [rows] = await rds.query(sql);
			if (rows[0]) {
				const row = rows[0];
				if (userid && userid != row.wk_userid) {
					// 排除userid为undefined
					// MTA-240823-I 企业微信用户可能会更改userid，在这里对企业微信用户的userid进行更新，以便通知模块等可正常工作
					const usql = `update tutem_mall.eb_wechat_user set wk_userid = '${userid}' where uid = ${row.uid}`;
					const [ures] = await rds.query(usql);
				}
				const rid = req.session.rid || "1604";
				const user = {
					cc_user_id: row.cc_user_id,
					weworkUserInfo: userInfo,
					type: `P${row.is_promoter}T${row.promoter_type}`,
					spread_uid: row.spread_uid,
					rep_uid: row.rep_uid,
					rid: row.rid,
					uid: row.uid,
					name: row.real_name,
					roles: row.roles,
					is_rep_id: row.is_rep_id,
					is_rep_code: row.is_rep_code,
					is_rep_team: row.is_rep_team,
					sql: sql,
				};
				const totemUID = user.uid;
				const ccUID = user.cc_user_id;
				let nextPath =
					req.session.dest_url || "https://mtx.totemlife.cn/";

				// 检查nextPath是否以特定的前缀开头
				const prefixes = [
					"https://mtx.totemlife.cn",
					"http://mtx.totemlife.cn",
				];
				const isMatch = prefixes.some((prefix) =>
					nextPath.startsWith(prefix)
				);
				// 如果匹配mtx，添加或更新'rft'参数
				if (isMatch) {
					let url = new URL(nextPath);
					if (!url.searchParams.has("rft")) {
						url.searchParams.set("rft", totemUID);
					}
					// 重新构造完整的URL
					nextPath = url.toString();
				}

				const info = `dest: [${nextPath}](${nextPath})`;
				await task.notifyUserAction(totemUID, info, "WXWORK-CALLBACK"); //这里只通知对应的代表

				// AUTH OKEY 在所有MTA APPS中都要改为按以下凭据为准，重启后无效 - 企业微信授权
				// USER-AUTH-3
				req.session.authUserInfo = {
					totemUID: user.uid,
					uid: user.uid,
					crmROLE: tutils.getCrmRole(user.roles),
					trialROLE: tutils.getTrialRole(user.roles),
					repID: user.is_rep_id,
					repCODE: user.is_rep_code,
					repTEAM: user.is_rep_team,
				};
				req.session.totemUID = totemUID;
				req.session.ccUID = ccUID;
				req.session.crmROLE = tutils.getCrmRole(user.roles);
				req.session.trialROLE = tutils.getTrialRole(user.roles);
				req.session.repID = user.is_rep_id;
				req.session.repCODE = user.is_rep_code;
				req.session.repTEAM = user.is_rep_team;

				req.session.user = user;
				req.session.rid = rid;
				req.session.weworkUserInfo = userInfo; //TBO
				req.session.save();

				delete req.session.dest_url;
				res.render("forward", { totemUID, nextPath, rid });
				return;
			}
		}

		logger.info("wk-auth failed: ", userInfo); //此时说明在eb_users表中不存在openid或者甚至openid本身未获取
		const message = "必须先绑定微信！<br>然后才能用企微登录MTX";
		const description =
			"复制下方链接到微信中打开<br>或在微信中识别下方二维码<br>即可完成绑定，如需帮助请<a href='/apps/c'><span style='color:#ff6f61;'>联系客服</span></a>";
		const finished_url =
			"https://totemlife.cn/apps/show-qr-card?subject=bind-wework-okey";
		const dest_param = `&dest=${encodeURIComponent(finished_url)}`;
		const userid_param = userid ? `&wk_userid=${userid}` : "";
		const openid_param = openid
			? `&wk_openid=${encodeURIComponent(openid)}`
			: "";
		const comment = `https://totemlife.cn/apps/bind-wework?${openid_param}${userid_param}${dest_param}`;
		//res.render('warn', { message, description, tips: comment });

		const background =
			"https://totemlife.cn/apps/public/background-ededed.png";
		const qrImg = await QRCode.toDataURL(comment, {
			type: "image/png",
			rendererOpts: { quality: 0.3 },
		});
		const refNo = "勿泄露给他人";

		res.render("show-card", {
			title: "企微用户认证提示",
			background,
			message,
			description,
			comment,
			qrImg,
			refNo,
		}); //render here
	}
});

app.get("/refer", async (req, res) => {
	const ruid = req.query.rid || "1606";
	const tuid = String(req.session.totemUID || 0); //这里特别用tuid，因为是字符串而非整数

	let dest_url = req.query.dest || "https://mtx.totemlife.cn/"; //这个query.dest是使用微信jssdk加上来的原始网页地址，不过用query.dest取的时候通常已decode
	dest_url = dest_url.replace(
		/mta\.totembio\.cn\/apps/g,
		"totemlife.cn/apps"
	);
	dest_url = dest_url.replace(/mta\.totembio\.cn/g, "mtx.totemlife.cn");
	if (req.query.dest_code)
		dest_url = await tutils.getMtaUrlByCode(
			req.query.dest_code,
			ruid,
			req.query.path || ""
		); //如果存在dest_code，则强制覆盖dest_url, 主要是要兼容

	const ua = req.headers["user-agent"] || "AnonymousUserAgent";
	const isWechat = ua.includes("MicroMessenger");
	const isWxwork = ua.includes("wxwork");
	const wlient = isWxwork
		? "wxwork-client"
		: isWechat
		? "wechat-client"
		: "other-client"; //注意isWxwork必须在前
	const options = { wlient: wlient, reqIp: req.ip };
	const options_str = JSON.stringify(options);
	logger.info(
		`MTA-IDX-REFER: ruid# ${ruid} | tuid# ${tuid} | ${wlient} ${req.ip} | ${dest_url} | ${req.url}`
	);

	//当用户被识别，且有推荐人，且不为同一用户
	if (tuid != "0" && ruid != "1606" && ruid != "666" && tuid != ruid) {
		const data = { tuid, ruid, dest_url };
		// 后面特地不用await，扔下即走
		task.recordUserAction(tuid, ruid, dest_url, "mta-refer", options_str);
		tutils.grantReferPoints(data);
	}

	req.session.rid = ruid;
	req.session.save();
	res.redirect(302, dest_url);
});

app.get("/show-daily-checkin-card", async function (req, res) {
	res.redirect("https://totemlife.cn/apps/daily-poster-entry"); //为兼容留着小程序客服对话框内的信息而保留
	return;
});

//CheckIncentive version2 -- 用于跳转到指定的博客地址
app.get("/ci2", async function (req, res) {
	const user = req.session.user;
	if (!user)
		return res.render("warn", {
			message: "用户未完成认证",
			description: "请在微信中打开",
		});

	const slug = (await tutils.getSlugBasedOnDate(moment())) || "expert-list";
	const url = req.query.url || "https://mtx.totemlife.cn/" + slug + "/";

	const uid = user.uid;
	const event = req.query.ec || req.query.event; //event code
	const response = await axios.post(
		"https://mall.totembio.cn/api/register_user_event",
		{ uid, event }
	);

	logger.info(`ci2: ${event} | ${url} `, response?.data);

	return res.redirect(302, url);
});

app.get("/sign-in", async (req, res) => {
	let totemUID = req.session.totemUID;
	if (totemUID) {
		let sql = `select uid, phone, is_promoter, promoter_type from tutem_mall.eb_user where uid = ${totemUID}`;
		const [rows] = await rds.query(sql);
		const {
			uid = 0,
			phone = "",
			promoter_type = 0,
			is_promoter = 0,
		} = rows[0] || { uid: 0, phone: "", promoter_type: 0, is_promoter: 0 };
		const tips = `uid#${totemUID} P${is_promoter}T${promoter_type} ${phone}`;
		if (phone && promoter_type > 0) {
			const email = phone + "@totembio.cn";
			//TBO-begin

			//TBO-end
			res.render("warn", {
				message: "订阅用户登录成功",
				icon: "weui-icon-success weui-icon_msg",
				description: `totemUID: ${totemUID} good phone: ${phone}`,
				tips,
			});
		} else {
			res.render("warn", {
				message: "订阅功能尚未开通",
				description: "请联系服务支持service@totembio.cn申请内测",
				tips,
			});
		}
	} else {
		res.render("warn", {
			message: "用户认证失败",
			description: "请在微信中打开",
		});
	}
});

app.get("/bind-wework", async (req, res) => {
	const totemUID = req.session.totemUID;
	if (totemUID) {
		const user = req.session.user;
		res.render("warn", {
			message: "绑定企业微信成功",
			icon: "weui-icon-success weui-icon_msg",
			description: "您现在可以回到企业微信直接使用",
			tips: `totemUID: #${totemUID}`,
		});
	} else {
		res.render("warn", {
			message: "用户认证失败",
			description: "请在微信中打开",
		});
	}
});

app.get("/stand-up", function (req, res) {
	const totemUID = req.session.totemUID;
	if (totemUID) {
		res.render("life");
	} else {
		res.render("warn", {
			message: "用户认证失败",
			description: "请在微信中打开",
		});
	}
});

app.get("/write-card", function (req, res) {
	const totemUID = req.session.totemUID;
	if (totemUID) {
		const subject = req.query.subject || "greeting";
		const template = tutils.getSubjectTemplate(subject);
		let { tips, title, background, message, description, comment } =
			template;
		const d = new Date();
		const day = d.toISOString().slice(0, 10); // 获取 ISO 格式的日期字符串
		background =
			"https://mall.totembio.cn/uploads/dcc/DCC-BG-" + day + ".png";
		res.render("write-card", {
			tips,
			title,
			background,
			message,
			description,
			comment,
		});
	} else {
		res.render("warn", {
			message: "用户认证失败",
			description: "请在微信中打开",
		});
	}
});

app.get("/ref-card", async function (req, res) {
	let {
		section = "asd",
		path = "",
		site = "mtx",
		siteName = "二代菌临床前沿",
		suffix = "card",
	} = req.query;
	const cid = req.query.cid || String(req.session.totemUID || 1653); //如果没有传入cid参数则使用session中的UID

	const subject = await tutils.getMtaSubject(section, cid, path);
	let {
		background,
		title,
		description,
		comment,
		code,
		slug,
		url,
		encodedUrl,
	} = subject;

	const mcid = cid * 10 + 123;
	const mdoi = new Date().getDate().toString().padStart(2, "0");
	const refNo = `${site}/${code}.${suffix}.${mdoi}.${mcid}`.toLowerCase();

	const qrData = `https://totemlife.cn/apps/refer?rid=${cid}&dest=${encodedUrl}`;
	const qrOptions = { type: "image/png", rendererOpts: { quality: 0.3 } };
	let qrImg = await QRCode.toDataURL(qrData, qrOptions);
	res.render("show-card", {
		title: siteName,
		background,
		message: title,
		description,
		comment,
		qrImg,
		refNo,
	}); //render here
});

app.get("/hello", function (req, res) {
	const user = {name: 'dong', age: 52};
	res.render("hello", {user} );
});

app.get("/download-excel", async function (req, res) {
	const sql =
		req.query.sql ||
		`SELECT date, recruited_count as r, first_order_count as f, qpo_count as q, buyer_count as b FROM tutem_mall.eb_spreader_daily_report WHERE spreader_uid = 1`;
	const filename = req.query.filename || `spreader-1-daily-report.xlsx`;

	//只支持SELECT 操作
	if (!sql.toLowerCase().includes("select")) {
		res.status(404).send("not support such sql request");
		return;
	}

	try {
		// 查询数据库中符合条件的记录
		const [rows] = await rds.query(sql);

		// 将查询结果转换为 Excel 数据表
		const workbook = xlsx.utils.book_new();
		const worksheet = xlsx.utils.json_to_sheet(rows);
		xlsx.utils.book_append_sheet(workbook, worksheet, "Report");

		// 生成 Excel 文件并发送给用户下载
		const buffer = xlsx.write(workbook, {
			type: "buffer",
			bookType: "xlsx",
		});
		res.setHeader(
			"Content-Disposition",
			`attachment; filename="${filename}.xlsx"`
		);
		res.setHeader(
			"Content-Type",
			"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
		);
		res.send(buffer);
	} catch (error) {
		console.error("Error generating Excel file:", error);
		res.status(500).send("Error generating Excel file");
	}
});

//这里是位兼容性而保留
app.get("/show-card", function (req, res) {
	res.redirect("https://totemlife.cn/apps/write-card");
});

//接受从write-card传过来的信息
app.post("/show-card", async function (req, res) {
	let {
		title = "祝福心语",
		message = "",
		description = "",
		comment = "",
		background = "https://totemlife.cn/apps/public/background-2.png",
	} = req.body;

	const cid = req.session.totemUID;
	const qrData = `https://totemlife.cn/apps/write-card?rid=${cid}`;
	const qrOptions = { type: "image/png", rendererOpts: { quality: 0.3 } };
	const qrImg = await QRCode.toDataURL(qrData, qrOptions);
	const refNo = "制作专属贺卡";

	res.render("show-card", {
		title,
		background,
		message,
		description,
		comment,
		qrImg,
		refNo,
	});
});

//如果传入qrData，则以qrData生成；否则根据qrImg生成；如果qrData和qrImg都没有传入，则用默认的write-card的网址生成二维码
app.get("/show-qr-card", async function (req, res) {
	const subject = req.query.subject || "greeting";
	const template = tutils.getSubjectTemplate(subject);
	let { title, background, message, description, comment, qrImg, refNo } =
		template;
	if (req.query.qrImg || req.query.qrData) {
		if (req.query.qrData)
			qrImg = await QRCode.toDataURL(qrData, {
				type: "image/png",
				rendererOpts: { quality: 0.3 },
			});
		if (req.query.qrImg) qrImg = qrImg;
	}
	res.render("show-card", {
		title,
		background,
		message,
		description,
		comment,
		qrImg,
		refNo,
	});
});

//这个路由在CRM中调用，但调用方式是间接的，通过在tutem_mall.eb_mta_materials数据表中配置到link和args数据而来
app.get("/generate-pdf", async (req, res) => {
	const section = req.query.section || "bgba";
	const cid = req.query.cid || String(req.session.totemUID || 1653);
	const caption =
		req.query.caption ||
		"MTA-PDF-05214 癫痫杂志 | doi: 10.7507/2096-0247.202301001";
	const pdfUrl =
		req.query.source ||
		"https://oss.foodtrust.cn/mta-ghost/202305/evbhqw.pdf";
	const codes = caption.split(" ")[0] || "";
	const site = codes.split("-")[0] || "mta";
	const suffix = codes.split("-")[1] || "pdf";
	const mdoi = codes.split("-")[2] || "0101";
	const mcid = cid * 10 + 123;

	// Generate a unique destFile for the PDF
	const destFile =
		`${site}.${section}-${mdoi}.${mcid}.${suffix}`.toLowerCase();
	// Write the modified PDF buffer to the local /tmp directory
	const destPath = `/home/www/mta-apps/public/mdoi/${destFile}`;

	try {
		// Check if the file already exists
		const exists = await fs.pathExists(destPath);
		if (exists) {
			res.setHeader(
				"Content-Disposition",
				'attachment; filename="' + destFile + '"'
			);
			res.sendFile(destPath);
			//res.download(destPath, destFile, (err) => { if (err) { console.error('Failed to download file:', err); res.status(500).send('Failed to download file'); } });
			return; // Exit the function early
		}

		const qrCodeUrl = `https://totemlife.cn/apps/refer?dest_code=${section}&rid=${cid}`;
		const comment = "Microecology Clinical Frontier";
		const tdoi =
			`tdoi: ${site}/${section}.${suffix}.${mdoi}.${mcid}`.toLowerCase();

		// Download the PDF file using axios
		const response = await axios.get(pdfUrl, {
			responseType: "arraybuffer",
		});
		const pdfBuffer = response.data;

		// Load the PDF document
		const pdfDoc = await PDFDocument.load(pdfBuffer, {
			ignoreEncryption: true,
		});

		// Embed the QR code on first page
		const pages = pdfDoc.getPages();
		for (let i = 0; i < pages.length; i++) {
			const qrCodeDataUrl = await QRCode.toDataURL(qrCodeUrl);
			const qrCodeImage = await pdfDoc.embedPng(qrCodeDataUrl);
			const page = pages[i];
			const { width, height } = page.getSize();
			const qrCodeDims = { width: 60, height: 60 };
			page.drawImage(qrCodeImage, {
				x: width - qrCodeDims.width - 56,
				y: height - qrCodeDims.height - 108,
				width: qrCodeDims.width,
				height: qrCodeDims.height,
			});

			let font = await pdfDoc.embedFont(StandardFonts.Helvetica);
			/*
			page.drawText(comment, {
				x: width - qrCodeDims.width - 20, y: height - qrCodeDims.height + 20,
				size: 8, font: font, color: rgb(0, 0, 0),
			});
			*/
			page.drawText(tdoi, {
				x: width / 2 - 48,
				y: height - 10,
				size: 8,
				font: font,
				color: rgb(0, 0, 0),
			});
			break;
		}

		modifiedPdfBytes = await pdfDoc.save();
		await fs.writeFile(destPath, modifiedPdfBytes);

		// Send the generated PDF file for download
		res.setHeader(
			"Content-Disposition",
			'attachment; filename="' + destFile + '"'
		);
		res.sendFile(destPath);
		//res.download(destPath, destFile, (err) => { if (err) { console.error('Failed to download file:', err); res.status(500).send('Failed to download file'); } });
	} catch (error) {
		console.error("Error generating PDF:", error);
		res.status(500).send("Error generating PDF");
	}
});

app.get("/patch-pdf", async (req, res) => {
	const pdfUrl =
		req.query.source || "https://totemlife.cn/apps/public/839-pd.pdf";
	const site = req.query.site || "mtx";
	const doi = req.query.doi || "20241029";
	const cid = req.query.cid || 7462;
	const suffix = req.query.suffix || "pdf";
	const qrUrl =
		req.query.qr ||
		"https://scan.totemlife.cn/mall-app-category?view=category&id=0&spreader_uid=7462";

	// Generate a unique destFile for the PDF
	const destFile = `${site}-${doi}-${cid}.${suffix}`.toLowerCase();
	// Write the modified PDF buffer to the local /tmp directory
	const destPath = `/home/www/mta-apps/public/mdoi/${destFile}`;

	try {
		// Check if the file already exists
		const exists = await fs.pathExists(destPath);
		if (exists) {
			res.setHeader(
				"Content-Disposition",
				'attachment; filename="' + destFile + '"'
			);
			res.sendFile(destPath);
			//res.download(destPath, destFile, (err) => { if (err) { console.error('Failed to download file:', err); res.status(500).send('Failed to download file'); } });
			return; // Exit the function early
		}

		//const qrCodeUrl = `https://totemlife.cn/apps/refer?dest_code=${section}&rid=${cid}`;
		const qrCodeUrl = qrUrl;
		const comment = "Microecology Clinical Frontier";
		const tdoi = `tdoi: ${site}/${suffix}.${doi}.${cid}`.toLowerCase();

		// Download the PDF file using axios
		const response = await axios.get(pdfUrl, {
			responseType: "arraybuffer",
		});
		const pdfBuffer = response.data;

		// Load the PDF document
		const pdfDoc = await PDFDocument.load(pdfBuffer, {
			ignoreEncryption: true,
		});

		// Embed the QR code on first page
		const pages = pdfDoc.getPages();
		for (let i = 0; i < pages.length; i++) {
			const qrCodeDataUrl = await QRCode.toDataURL(qrCodeUrl);
			const qrCodeImage = await pdfDoc.embedPng(qrCodeDataUrl);
			const page = pages[i];
			const { width, height } = page.getSize();
			const qrCodeDims = { width: 60, height: 60 };
			page.drawImage(qrCodeImage, {
				x: width - qrCodeDims.width - 60,
				y: height - qrCodeDims.height - 60,
				width: qrCodeDims.width,
				height: qrCodeDims.height,
			});

			let font = await pdfDoc.embedFont(StandardFonts.Helvetica);
			/*
			page.drawText(comment, {
				x: width - qrCodeDims.width - 20, y: height - qrCodeDims.height + 20,
				size: 8, font: font, color: rgb(0, 0, 0),
			});
			*/
			page.drawText(tdoi, {
				x: width / 2 - 48,
				y: height - 10,
				size: 8,
				font: font,
				color: rgb(0, 0, 0),
			});
			break;
		}

		modifiedPdfBytes = await pdfDoc.save();
		await fs.writeFile(destPath, modifiedPdfBytes);

		// Send the generated PDF file for download
		res.setHeader(
			"Content-Disposition",
			'attachment; filename="' + destFile + '"'
		);
		res.sendFile(destPath);
		//res.download(destPath, destFile, (err) => { if (err) { console.error('Failed to download file:', err); res.status(500).send('Failed to download file'); } });
	} catch (error) {
		console.error("Error generating PDF:", error);
		res.status(500).send("Error generating PDF");
	}
});

app.get("/login", async (req, res) => {
	//强制退出用户登陆状态
	const title = req.session.title || "微信扫码登陆";
	if (req.session.user) delete req.session.user;

	const tempUUID = req.session.temp_uuid || uuid.v4();
	req.session.temp_uuid = tempUUID;

	const record = await tutils.registerMpAuth(tempUUID);
	const { temp_uuid = tempUUID, channel_id = 0, qr_code_url = "" } = record;
	const message = "关注图腾生命医学完成登陆";
	const tips = "等待微信扫码";
	return res.render("login", {
		temp_uuid,
		channel_id,
		qr_code_url,
		title,
		message,
		tips,
	});
});

app.post("/logger", (req, res) => {
	logger.info(`logger called: `, req.body);
	res.json({ message: "Data received and logged." });
});

app.get("/download-mall-card", async (req, res) => {
	const cid = req.query.cid || "1653";
	const qrt = req.query.qrt || "weapp_839_card";
	try {
		const apiUrl =
			"https://mall.totembio.cn/api/get_mall_materials?cid=" +
			cid +
			"&qrt=" +
			qrt; // 替换为你的API URL

		const response = await axios.get(apiUrl);
		const imageUrl = response.data; // API响应数据直接回复image_url

		const imageResponse = await axios.get(imageUrl, {
			responseType: "arraybuffer",
		});

		const fileName = path.basename(imageUrl); // 下载文件的名称
		const filePath = "/home/www/mta-apps/public/mall-card/" + fileName; // 下载文件的保存路径

		fs.writeFileSync(filePath, imageResponse.data, "binary");

		res.sendFile(filePath, (err) => {
			if (err) {
				console.error(err);
			}
			//fs.unlinkSync(filePath); // 删除保存的文件
		});
	} catch (error) {
		console.error(error);
		res.status(500).send("Internal Server Error");
	}
});

app.get("/daily-poster-entry", (req, res) => {
	const code = req.query.code || "weapp-cover";
	const imageUrl = "https://totemlife.cn/apps/public/daily-poster-entry.png";
	res.setHeader("Content-Type", "image/png"); //设置响应头
	https
		.get(imageUrl, (response) => {
			response.pipe(res);
		})
		.on("error", (err) => {
			console.error(err);
			res.status(500).send("Internal Server Error");
		});
});

app.get("/default-image", (req, res) => {
	const code = req.query.code || "default-bg";
	const images = {
		"gray-bg": "https://totemlife.cn/apps/public/background-ededed.png",
		"default-bg": "https://totemlife.cn/apps/public/background-flowers.jpg",
		"weapp-cover":
		"https://totemlife.cn/apps/public/weapp-cover-300x300.png",
		"chat-h5-bg":
		"https://totemlife.cn/apps/public/chat-h5-bg-1920x1200.jpg",
	};
	const imageUrl = images[code] || "https://totemlife.cn/apps/public/weapp-cover-300x300.png";
	res.setHeader("Content-Type", "image/png"); //设置响应头
	https
		.get(imageUrl, (response) => {
			response.pipe(res);
		})
		.on("error", (err) => {
			console.error(err);
			res.status(500).send("Internal Server Error");
		});
});

app.get("/clear", (req, res) => {
	delete req.session.totemUID;
	delete req.session.rid;
	delete req.session.rud; //for downward compatiblity
	delete req.session.dest_url;
	delete req.session.wechatUserInfo;
	delete req.session.user;
	const destUrl = req.query.dest
		? decodeURIComponent(req.query.dest)
		: "https://totemlife.cn/apps/";
	res.render("clear", { destUrl });
});

app.get("/support", (req, res) => {
	res.redirect("https://support.qq.com/products/615894");
});

//暂时不能放入api中，因为api需要session授权
app.post("/assign-task", async (req, res) => {
	const {
		assignee_type,
		assignee_id,
		task_code,
		task_title,
		channels,
		entry,
		description,
		status,
		badge,
	} = req.body;
	console.log(
		assignee_type,
		assignee_id,
		task_code,
		task_title,
		channels,
		entry,
		description,
		status,
		badge
	);

	if (process.env.RDS_AGENT != badge)
		return res.status(403).json({ message: "access denied" });

	// 调用 assignTask 函数
	const taskId =
		(await task.assignTask(
			assignee_type,
			assignee_id,
			task_code,
			task_title,
			channels,
			entry,
			description,
			status
		)) || 0;
	// 返回任务 ID
	res.status(200).json({ taskId });
});

app.get("/download-doc", (req, res) => {
	const fileName = req.query.file || "0f1e2202403181909549663.pdf";
	const remoteFileUrl = `https://oss.foodtrust.cn/${fileName}`;
	res.render("warn", {
		title: "医用二代菌临床前沿",
		message: "患者的笑容是我们最好的回报",
		description: "欢迎来到营养微生态临床应用最前沿",
		icon: "weui-icon-success weui-icon_msg",
		docLink: remoteFileUrl,
		docName: fileName,
	});
});

app.get("/download-doc2", async (req, res) => {
	const fileName = req.query.file || "0f1e2202403181909549663.pdf";
	const cacheDir = path.join(__dirname, "cache");
	const localFilePath = path.join(cacheDir, fileName);
	const remoteFileUrl = `https://oss.foodtrust.cn/${fileName}`;

	try {
		// 检查文件是否在本地缓存目录中存在
		if (fs.existsSync(localFilePath)) {
			// 如果文件存在，将其发送给用户进行下载
			res.setHeader("Location", "https://totemlife.cn/"); // 设置重定向头
			res.download(localFilePath, fileName);
		} else {
			// 如果文件不存在，从远程 URL 下载文件
			const response = await axios({
				url: remoteFileUrl,
				method: "GET",
				responseType: "stream",
			});

			// 创建缓存目录（如果不存在）
			if (!fs.existsSync(cacheDir)) {
				fs.mkdirSync(cacheDir);
			}

			// 将下载的文件保存到本地缓存目录
			const writer = fs.createWriteStream(localFilePath);
			response.data.pipe(writer);

			// 等待文件写入完成
			await new Promise((resolve, reject) => {
				writer.on("finish", resolve);
				writer.on("error", reject);
			});

			// 将下载的文件发送给用户进行下载
			res.setHeader("Location", "https://totemlife.cn/"); // 设置重定向头
			res.download(localFilePath, fileName);
		}
	} catch (error) {
		console.error("下载文件时出错:", error);
		res.status(500).send("下载文件时出错");
	}
});

// 二维码生成服务的路由
app.get("/create-qr-code", (req, res) => {
	// 解析请求参数
	const { data, size } = req.query;

	// 验证参数
	if (!data || !size) {
		return res
			.status(400)
			.send("Missing required query parameters: data and size");
	}

	// 生成二维码
	QRCode.toBuffer(
		data,
		{ type: "image/jpeg", width: size, margin: 2 },
		(err, buffer) => {
			if (err) {
				console.error(err);
				return res.status(500).send("Error generating QR code.");
			}

			// 将二维码以Buffer的形式直接发送给客户端
			res.set("Content-Type", "image/jpeg");
			return res.send(buffer);
		}
	);
});

// 二维码生成服务的路由
app.get("/create-qr-code-link", (req, res) => {
	// 解析请求参数
	const { data, size, filename } = req.query;

	// 验证参数
	if (!data || !size) {
		return res
			.status(400)
			.send("Missing required query parameters: data and size");
	}

	const publicPath = `./public/${filename}`;
	// 生成二维码
	QRCode.toFile(
		publicPath,
		data,
		{ type: "image/jpeg", width: size, margin: 2 },
		(err) => {
			if (err) {
				console.error(err);
				return res.status(500).send("Error generating QR code.");
			}

			// 返回二维码图片的URL
			return res.send(`https://totemlife.cn/apps/public/${filename}`); //https://apps.totemlife.cn/public/foo.jpg
		}
	);
});

//good, except miniProgram
app.get('/_blank', (req, res) => {
	const encodedUrl = req.query.url;
	if (!encodedUrl) return res.status(400).send('Missing URL parameter');
	const decodedUrl = new URL(encodedUrl).href;

    res.render("go",{decodedUrl});
});


//由于在新品平台在webview微信H5中它会将:path中的一起解码变成/go2/http://totemlife.cn...，这样
app.get('/go2/:path', (req, res) => {
    const path = req.params.path;
    const baseUrl = 'https://totemlife.cn/apps/';
    const decodedUrl = new URL(path, baseUrl).href;  // 自动拼接基础路径

    res.render('go2',{decodedUrl});
});

//为在网页中嵌入新平台的统一搜索而嵌入
app.get('/search4', (req, res) => {
    const key = req.query.searchValue || '';
    //const key = decodeURIComponent(req.query.s || '') // eg. s=encoded(key), key: searchValue=ERP v=ERP t=tag
    //未来其实是要在这里区分开是去ihealthify还是totemlife，如果是去totemlife，url可以换成跳转小程序
    const url = `https://ihealthify.com/pages/columnGoods/goods_search_con/index?searchValue=${key}`;
    const page = `/pages/columnGoods/goods_search_con/index?searchValue=${key}`;
    console.log('search4 called, url:', url, ' page:', page);
    res.render('search4',{url,page});
});

app.listen(2371);
